package com.sff.site.tree {
	import com.sff.site.abstract.AbstractTree;
	import com.sff.site.core.INode;
	import com.sff.site.core.IPath;
	import com.sff.site.events.NodeEvent;
	import com.sff.site.node.NodeDescriptor;
	
	import flash.events.Event;
	import flash.external.ExternalInterface;
	import flash.net.URLVariables;

	public class SiteTree extends AbstractTree {
		
		public static const PATH_CHANGE : String = "treePathChange";
		public static const TREE_CHANGE : String = "treeActivationChange";
				
		private var _pendingPath 		: SiteTreePath;
		private var _realPath 			: SiteTreePath;
		private var _activePath 		: IPath;
		private var _activeDPath 		: IPath;
		private var _lastPathNotified 	: IPath;
		
		public function SiteTree( device : String ) {
			super( new RootNode( device ) );
		}
		
		public function get activePath() : IPath {
			return _activePath;
		}
		
		public function get activeDPath() : IPath {
			return _activeDPath;
		}
		
		public function set activePath( path : IPath ) : void {
			_setParams( path );
			
			var exist : Boolean = path.appendDefaults().nodeExist() && path.nodeExist();
			
			if(!exist) {
				_pendingPath = path as SiteTreePath;
			} else {
				_pendingPath = null;	
			}
			
			pendingMode(!exist );
			
			var oldPath : IPath = _activeDPath;
			_activePath = path;
			
			if( _activeDPath && _activeDPath.nodeExist() ) {
				_activeDPath.toNode().removeEventListener( NodeEvent.DEFAULT_CHANGE, onDefaultChangeHandler );
			}
			
			_activeDPath = _activePath.appendDefaults();
			
			if( _activeDPath.nodeExist() ) {
				_activeDPath.toNode().addEventListener( NodeEvent.DEFAULT_CHANGE, onDefaultChangeHandler );
			}
			
			_fireActivation( oldPath );
			_unsetParams( oldPath );
			
			var change : Boolean = ( _realPath && exist && ! _realPath.equals( _activeDPath ) && _activeDPath.nodeExist() );
			_realPath = _activeDPath.cleanup( ) as SiteTreePath;
			
			
			dispatchEvent( new Event( PATH_CHANGE ) );
			
			if( change ) {				
				if( ! _lastPathNotified || !_lastPathNotified.equals( _activePath ) ) {					
						
					_lastPathNotified = _activePath;
				}
				dispatchEvent( new Event( TREE_CHANGE ) );
			}
		}
		
		private function onDefaultChangeHandler(evt: NodeEvent) : void {
			activePath = activePath;
		}
		
		private function _setParams( path : IPath ) : void {
			if( path == null ) {
				return;
			}
			
			var a : Array = path.nodes();
			var  p : URLVariables = path.getParams();
			for (var i : int = 0; i < a.length; i++) 
				( a[ i ] as INode ).setParams( p );
		}

		private function _unsetParams( path : IPath ) : void {
			if( !path ) return;
			var oldList : Array = path.nodes( path.diff( _activeDPath ) );
			oldList.shift();
			for (var i : int = 0; i < oldList.length; i++) 
				( oldList[ i ] as INode ).setParams( null );
		}


		override protected function newNode( subDesc : NodeDescriptor, parent : INode ) : INode {
			var node : TreeNode = new TreeNode( parent as TreeNode );
			node.describe( subDesc );
			return node;
		}
		
		private function _fireActivation( oldPath : IPath = null ) : void {
			if( ! oldPath ) {
				var ns : Array = _activeDPath.nodes();
				ns.shift();
				_fireInNodeList( ns, NodeEvent.CHANGE, true );
				return;
			}
			var diffNode : INode = oldPath.diff( _activeDPath ) ;
			if( ! diffNode ) throw new Error( "SiteTree - _fireActivation problem : no diff but not equals !!!" );
			
			var oldList : Array = oldPath.nodes( diffNode );
			var newList : Array =  _activeDPath.nodes( diffNode );

			_processActivate( oldList, newList );

			oldList.shift();
			newList.shift();
			
			var pcc : Boolean = newList.length > 0;
			_fireInNodeList( oldList, NodeEvent.CHANGE, false, pcc );
			if( pcc )
				_fireInNodeList( newList, NodeEvent.CHANGE, true );
			else if( oldList.length > 0 )
				diffNode.dispatchEvent( new NodeEvent( NodeEvent.CHILD_CHANGE, oldList[0] ) );
		}
		
		
		private function _processActivate(oldList : Array, newList : Array) : void {
			var l : int = oldList.length;
			var i : int = 0;
			var anode : TreeNode;
			
			for (i = 0; i < l; i++) {
				anode = oldList[ i ] as TreeNode;
				anode.setActive( false );
			}
			l = newList.length;
			i = 0;
			for (i = 0; i < l; i++) {
				anode = newList[ i ] as TreeNode;
				anode.setActive( true );
			}
		}
		
		
		private function _fireInNodeList( nList : Array, eventType : String, state : Boolean, pendingCC : Boolean = true ) : void {
			var l : int = nList.length;
			if( l == 0 ) return;
			var tnode : INode = nList[ l-1 ];
			var anode : TreeNode;
			var i : int = 0;
			for (i = 0; i < l; i++) {
				anode = nList[ i ] as TreeNode;
				anode.dispatchEvent( new NodeEvent( eventType , tnode, true ) );

				if( !pendingCC || i != 0 || state ) {
					anode.parent().dispatchEvent( new NodeEvent( "child"+eventType , anode , true) );
				}
			}

			while( --i > -1 ) {
				anode = nList[ i ] as TreeNode;
				anode.dispatchEvent( new NodeEvent( eventType , tnode ) );
				
				if( !state && i != 0 ){
					anode.parent().dispatchEvent( new NodeEvent( "child"+eventType , anode ) );
				} else if( state ) {
					anode.parent().dispatchEvent( new NodeEvent( "child"+eventType , anode ) );
				}
			}
		}
		
		private function pendingMode( flag : Boolean ) : void {
			removeEventListener( NodeEvent.CHILD_ADDED, onChildAddedHandler );
			
			if(flag) {
				addEventListener( NodeEvent.CHILD_ADDED, onChildAddedHandler );
			}	
		}
		
		private function onChildAddedHandler(evt : NodeEvent) : void {
			if( _pendingPath != null ) {
				var cp : IPath = _pendingPath;
				if( _pendingPath.appendDefaults().nodeExist() && _pendingPath.nodeExist() ) { 
					_pendingPath = null;
					pendingMode( false );
				}
				activePath = cp;
			}
		}
	}
}

import com.sff.site.tree.TreeNode;

class RootNode extends TreeNode {

	public function RootNode( device : String ) {
		super( null );
		_id = device;
	}
	
}